/**
 * @author: welling
	Error({
		httpCode:hcode,
		httpText:htext,
		content:text
	});
 * 
 */
Weifox.net={
	/**
     * if download succefully
     * return a fileinput stream
     * else return a null value
     * from https://developer.mozilla.org/en/Code_snippets/Downloading_Files
     * modified by welling okey007@126.com
     **/
    getImageFromURL: function(url){
        var ioserv = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
        var channel = ioserv.newChannel(url, 0, null);
        var stream = channel.open();
        if (channel instanceof Components.interfaces.nsIHttpChannel && channel.responseStatus != 200) {
            return null;///note this 
        }
        var bstream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
        bstream.setInputStream(stream);
        return bstream;
    },
	/**
	 * 
	 * @param {Object} code http 代码
	 */
	getError:function(code){
		//var n=parseInt(code);
		if(code>=300&&code<400){
			return "你发送的请求已重定向"
		}else if(code>=400&&code<500){
			return "帐号或密码错误,或者该帐号还未授权给本扩展"
		}else if(code>=500){
			return "服务器超时错误,或者你的网络连接不稳定"
		}else return "Just Error"
	},
	/**
	 * 将Data加工成一个适用于OAuth通信的Request数据
	 * @param {Object} data
	 * @param {Object} ak	appkey
	 * @param {Object} ks	keysecret
	 * @param {Object} o	oauth
	 * @param {Object} h	header 是否在header,默认True
	 */
	makeOAuth:function(data,ak,ks,o,h){
		var ctime= Math.floor(new Date().getTime()/1000);
		var oauth={
			 oauth_callback:"null",
			 oauth_consumer_key:ak,
//			 oauth_consumer_secret:ks,
			 oauth_timestamp:ctime,
			 oauth_version:'1.0',
			 oauth_nonce:'time'+ctime ,
	         oauth_signature_method:'HMAC-SHA1'
		}
		var secret={
			oauth_consumer_secret:ks,
			oauth_token_secret:""
		}
		if(!o)o={};
		//将传入的值覆盖默认的Oauth
		//如果传入为空时则表示要删除这个属性
		if(o.oauth_token_secret){
			secret.oauth_token_secret=o.oauth_token_secret;
			delete o.oauth_token_secret;
		}
		for(var i in o){
			var v=o[i];
			if (v != '') {
				oauth[i] = v;
			}
			else {
				if (oauth[i]) 
					delete oauth[i];
			}
		}
		var oparas=new Array();
		for(var i in oauth){
			oparas.push([i,oauth[i]]);
		}
		var mixparas=new Array();
		
		if (data.args) {
			for(var i=0;i<data.args.length;i++){
				var a=data.args[i];
				mixparas.push([a[0],this.rfcEncode(a[1])]);
				//mixparas.push([a[0],a[1]]);
			}
		}else{
			data.args=[];
		}
		
		mixparas=mixparas.concat(oparas);
		
		var baseString=this.generateBaseString(mixparas,data.url,data.method);
		var macKey=secret.oauth_consumer_secret+'&'+secret.oauth_token_secret;
		
        var oauth_signature=this.rfcEncode(Weifox.util.HMAC_SHA1.b64_hmac_sha1(macKey,baseString));

		oparas.push(['oauth_signature',oauth_signature]);
		mixparas.push(['oauth_signature',oauth_signature]);
			//h=false;
			
		if(!data.stream){
			this.encodeArgs(data.args);
		}
		if(h||h==undefined){
			var str = 'OAuth ';
			var i = 0;
			for (; i < oparas.length-1; i++) {
				str += oparas[i][0] + '="' + oparas[i][1] + '",'
			}
			str += oparas[i][0] + '="' + oparas[i][1] +'"';
			data.headers = [['Authorization', str]];
			
		}else{
			//----TODO 修复:新浪传图不能用这个方式
			data.args = oparas.concat(data.args);
			//alert(data.args.toString());
		}
	},
	/**
	 * 将Data加工成一个适用于Basic通信的Request数据
	 * @param {Object} data
	 * @param {Object} un
	 * @param {Object} pw
	 */
	makeBasic:function(data,un,pw){
		
		data.headers=[['Authorization','Basic '+Weifox.util.basic64Encode(un+":"+pw)]];
	},
	JSStringToUTF8:function(aStr) {
	  var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
	                    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
	
	  converter.charset = "utf-8" ;
	  return converter.ConvertFromUnicode(aStr);
	},
	rfcEncode: function(s) {
		var n;
        n = encodeURIComponent(s);
        n = n.replace(/\!/g, "%21");
        n = n.replace(/\*/g, "%2A");
        n = n.replace(/\'/g, "%27");
        n = n.replace(/\(/g, "%28");
        n = n.replace(/\)/g, "%29");
        return n;
    },
	rfcDecode:function(s){
		var n=s;
		n = n.replace("%21","!");
        n = n.replace("%2A","*");
        n = n.replace("%27","'");
        n = n.replace("%28","(");
        n = n.replace("%29",")");
		n = decodeURIComponent(n);
		return n;
	},
	/**
	 * 生成一个与OAuth相关的Base字符串
	 * @param {Object} mixParas
	 * @param {Object} url
	 * @param {Object} method
	 */
	generateBaseString:function(mixparas,url,m){
		var method='';
		if(m){
			method=m.toUpperCase();;
		}else{
			method='POST';
		}
		mixparas.sort();
		var base=method+'&'+ this.rfcEncode(url) + '&';
		var newp=new Array();
		for(var i=0;i<mixparas.length;i++){
			newp.push(this.rfcEncode(mixparas[i][0])+'%3D'+this.rfcEncode(mixparas[i][1]));
		}
		base+=newp.join('%26');
//		alert('BASE string:\n'+base);
		return base;
	},
	/**
	 * usage:
	 * 		var request=new Weifox.net.Request();
			var data={
				args:'source=1920019471',
				url:'http://api.t.sina.com.cn/statuses/friends_timeline.json',
				success:function(msg){
					if(!msg)
						alert('error');
					//alert(msg);
				},
				error:function(text){}
			};
			request.send(data);
	 */
//https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIHttpChannel
//https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIChannel
//https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIRequest	
//https://developer.mozilla.org/en/HTTP/HTTP_response_codes
	Request:function(){
        this.httpChannel = null;
        this.channel = null;
		this.responseText='';
		this.mCallbackFunc=function(){}
		this.headers=new Array();
	},
	boundary:"-----------weifoxseparator",
	/**
	 * 
	 * @param {Object} aName
	 * @param {Object} aValue
	 * @param {Object} aMultiplexInputStream
	 */
	addText2Stream: function(aName, aValue, aMultiplexInputStream){
        var openSeparator =  "\r\n--" + Weifox.net.boundary + "\r\n";
        
        var openSeparatorStream = Components.classes['@mozilla.org/io/string-input-stream;1'].createInstance(Components.interfaces.nsIStringInputStream);
        openSeparatorStream.setData(openSeparator, -1);
        aMultiplexInputStream.appendStream(openSeparatorStream);
        var mimeInputStream = Components.classes["@mozilla.org/network/mime-input-stream;1"].createInstance(Components.interfaces.nsIMIMEInputStream);
//        mimeInputStream.addContentLength = true;
//      mimeInputStream.addHeader("Content-Type", "application/x-www-form-urlencoded");
        mimeInputStream.addHeader("Content-Disposition", "form-data; name=\"" + aName + "\"");
        
        var valueInputStream = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
        valueInputStream.setData(aValue, -1);
        mimeInputStream.setData(valueInputStream);
        aMultiplexInputStream.appendStream(mimeInputStream);
    },
	/**
	 * 
	 * @param {Object} aPartName
	 * @param {Object} aFileStream
	 * @param {Object} aMultiplexInputStream
	 */
    ////from http://svn.firedocs.org/filedetails.php?repname=firedocs&path=%2Ftrunk%2Fsrc%2Fextension%2Fchrome%2Fcontent%2Fnet%2FAsyncNetworkService.js&rev=44
    addImage2Stream: function(aPartName, aFileStream, aMultiplexInputStream){
        var openSeparator =  "\r\n--" + Weifox.net.boundary + "\r\n";
        
        var bufferedInputStream = Components.classes["@mozilla.org/network/buffered-input-stream;1"].createInstance(Components.interfaces.nsIBufferedInputStream);
        
        bufferedInputStream.init(aFileStream, 4096);
        var openSeparatorInputStream = Components.classes['@mozilla.org/io/string-input-stream;1'].createInstance(Components.interfaces.nsIStringInputStream);
        openSeparatorInputStream.setData(openSeparator, -1);
        aMultiplexInputStream.appendStream(openSeparatorInputStream);
        var mimeInputStream = Components.classes["@mozilla.org/network/mime-input-stream;1"].createInstance(Components.interfaces.nsIMIMEInputStream);
        
		//mimeInputStream.addHeader("Content-Type", "image/png");
		
        mimeInputStream.addHeader("Content-Disposition", "form-data; name=\"" + aPartName + "\"; filename=\"" + /*aFileStream.leafName*/ 'from_Weifox_picture' + "\"");
        //mimeInputStream.addContentLength = true;
        mimeInputStream.setData(bufferedInputStream);
        aMultiplexInputStream.appendStream(mimeInputStream);
    },
	encodeArgs:function(args){
		if(args)
		for(var i=0;i<args.length;i++){
			args[i][0]=this.rfcEncode(args[i][0]);
			args[i][1]=this.rfcEncode(args[i][1]);
		}
	},
	decodeArgs:function(args){
		if(args)
		for(var i=0;i<args.length;i++){
			args[i][0]=this.rfcDecode(args[i][0]);
			args[i][1]=this.rfcDecode(args[i][1]);
		}
	},
};

Weifox.net.Request.prototype={
	////listeners
    onStartRequest: function(aRequest, aContext){
        this.responseText = "";
    },
    onDataAvailable: function(aRequest, aContext, aStream, aSourceOffset, aLength){
        var scriptableInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
        scriptableInputStream.init(aStream);
        this.responseText += scriptableInputStream.read(aLength);
        
    },
	//all http codes:https://developer.mozilla.org/en/HTTP/HTTP_response_codes
    onStopRequest: function(aRequest, aContext, aStatus){
		var hcode=this.getHttpCode();
		var htext=this.getHttpText();
		//TODO:handle error
		//        if (Components.isSuccessCode(aStatus)) {}
		var text=decodeURIComponent(escape(this.responseText));
//		alert('re:'+text);
		if(hcode>=200&&hcode<=399){
			if (this.mCallbackFunc) {
				try {
					var json=JSON.parse(text);
					this.mCallbackFunc(json);
				}catch(e){					
					this.mCallbackFunc(text);
				}
			}
		}
        else {
            // request failed
			if(this.onError)
			this.onError({
				httpCode:hcode,
				httpText:htext,
				url:this._url,
				content:text
			});
        }
        this.channel = null;
        Weifox.util.observer.removeObserver(this, 'http-on-modify-request');

    },
    
    // nsIChannelEventSink
    onChannelRedirect: function(aOldChannel, aNewChannel, aFlags){
        // if redirecting, store the new channel
        this.channel = aNewChannel;
    },
    // nsIInterfaceRequestor
    getInterface: function(aIID){
        try {
            return this.QueryInterface(aIID);
        }
        catch (e) {
            throw Components.results.NS_NOINTERFACE;
        }
    },
    
    // nsIProgressEventSink (not implementing will cause annoying exceptions)
    onProgress: function(aRequest, aContext, aProgress, aProgressMax){},
    onStatus: function(aRequest, aContext, aStatus, aStatusArg){},
    // nsIHttpEventSink (not implementing will cause annoying exceptions)
    onRedirect: function(aOldChannel, aNewChannel){},
    
    observe: function(subject, topic, data){
        //set cookie to "" or firefox will add the jessionid 
        ///automatically when connecting to sina
        if (subject == this.httpChannel) {
            if (topic == "http-on-modify-request") {
				var http=this.httpChannel;
                http.setRequestHeader("Cookie", "", false);
				
				var h = this.headers;
				if(h)
				for (var i = 0; i < h.length; i++) {
					//alert('header:\n'+h[i][0]+'\n'+h[i][1]);
					http.setRequestHeader(h[i][0], h[i][1], false);
				}
            }
        }
        
    },
	
	///listener end

	abort:function(){
		this.channel.cancel(Components.results.NS_BINDING_ABORTED);
	},
    setHeader: function(name, value){
        this.httpChannel.setRequestHeader(name, value, false);
    },
	getHeader:function(key){
		return this.httpChannel.getResponseHeader(key);
	},
	
	getHttpCode:function(){
		var s='';
		try {
			s = this.httpChannel.responseStatus;
		}catch(e){
			s='500';
		}
		return s; 
	},
	getHttpText:function(){
		var s='';
		try {
			s = this.httpChannel.responseStatusText;
		}catch(e){
			s='Http wrong!';
		}
		return s;
	},
	
    setChannel: function(url){
		this._url=url;
        var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
        var uri = ioService.newURI(url, null, null);
        this.channel = ioService.newChannelFromURI(uri);
        this.httpChannel = this.channel.QueryInterface(Components.interfaces.nsIHttpChannel);
    },
    /**
     *
     * @param {Object} data
     * data contains
     * 	stream //for multi-part data.stream 现改为一个只放置流的对象 data.stream={pic:指向文件}
     *  为True时自动将Args里的所有参数加进Stream里
     *  
     * 	args [['',''],['x','x']] //a string array
     * 	url 
     *  method='POST'   upper/lower case accepted
     *  redirectionLimit=0
     *  headers =[[x,x],[x,x]] eg.. data.headers = [['Authorization', str]]; => setHeader
     *  
     *  
     */
    send: function(data){

		if (!data.url) {
			throw new Error("Weifox.net.Request needs a url! ");
			return;
		}
			
		if (data.method && data.method.toUpperCase() == 'GET') {
			if(data.args){
				var msg = '';
				var i = 0;
				for (; i < data.args.length - 1; i++) {
					msg += data.args[i].join('=') + '&';
				}
				if(i<data.args.length)
					msg += data.args[i].join('=');
				
				this.setChannel(data.url+'?'+msg);
				//alert(this._url);
			}else
				this.setChannel(data.url);
			
		}
		else {
			this.setChannel(data.url);
			
			var upChannel = this.channel.QueryInterface(Components.interfaces.nsIUploadChannel);
			if (data.stream) {
				var stream=Components.classes["@mozilla.org/io/multiplex-input-stream;1"].createInstance(Components.interfaces.nsIMultiplexInputStream).QueryInterface(Components.interfaces.nsIInputStream);;
				var net=Weifox.net;
				for (var i = 0; i < data.args.length; i++) {
					net.addText2Stream(data.args[i][0],data.args[i][1] ,stream);
				}
				for(var x in data.stream){
					net.addImage2Stream(x, data.stream[x], stream);
				}
				
				var closeSeparator = "\r\n--" + net.boundary + "--";
				var closeSeparatorInputStream = Components.classes['@mozilla.org/io/string-input-stream;1'].createInstance(Components.interfaces.nsIStringInputStream);
				closeSeparatorInputStream.setData(closeSeparator, -1);
				stream.appendStream(closeSeparatorInputStream);
				
				upChannel.setUploadStream(stream, null, -1);
				this.setHeader("Content-Length", stream.available() - 2);
				this.setHeader('Content-Type', 'multipart/form-data; boundary=' + net.boundary);
			}
			else 
				if (data.args) {
					var upStream = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
					var msg = '';
					var i = 0;
					for (; i < data.args.length - 1; i++) {
						msg += data.args[i].join('=') + '&';
					}
					if(i<data.args.length)
						msg += data.args[i].join('=');
					//alert(msg);
					upStream.setData(msg, msg.length);
					upChannel.setUploadStream(upStream, "application/x-www-form-urlencoded", -1);
				}
		}
		//各个参数的判断
        if(!data.method)
        	this.httpChannel.requestMethod = 'POST';
		else {
			this.httpChannel.requestMethod = data.method.toUpperCase();
		}
		if(data.redirectionLimit&&data.redirectionLimit>0){
			this.httpChannel.redirectionLimit=data.redirectionLimit;
		}else this.httpChannel.redirectionLimit=0;
		
		if(data.success)
			this.mCallbackFunc=data.success;
		if(data.error){
			this.onError=data.error;
		}
		if (data.headers) {
			this.headers = data.headers;
		}
		
        Weifox.util.observer.addObserver(this, "http-on-modify-request", false);
		this.channel.notificationCallbacks=this;	
        this.channel.asyncOpen(this, null);
        if (data.stream) {
            //data.stream.close();
        }

    }
}
